package com.dahuyou.tryy.too.simulate.interpreter.parse.clazz.clazzfile;

import com.dahuyou.tryy.too.simulate.interpreter.parse.clazz.clazzfile.attributeinfo.AttributeInfo;
import com.dahuyou.tryy.too.simulate.interpreter.parse.clazz.clazzfile.constantpool.ConstantPool;

/**
 * class字节码映射类
 * ClassFile {
 *      u4　　　　　　　magic;
 *      u2　　　　　　　minor_version;
 *      u2　　　　　　　major_version;
 *      u2　　　　　　　constant_pool_count;
 *      cp_info　　　　 constant_pool[constant_pool_count-1];
 *      u2　　　　　　　access_flags;
 *      u2　　　　　　　this_class;
 *      u2　　　　　　　super_class;
 *      u2　　　　　　　interfaces_count;
 *      u2　　　　　　　interfaces[interfaces_count]
 *      u2　　　　　　　fields_count;
 *      field_info 　　　fields[fields_count];
 *      u2　　　　　　　methods_count;
 *      method_info　　methods[methods_count];
 *      u2　　　　　　　attributes_count;
 *      attribute_info　　attributes[attributes_count]
 * }
 */
public class ClassFile {
    // minor version，小版本，一般是0
    private int minorVersion;
    // major version，主版本，8 52 其他依次+1和-1
    private int majorVersion;
    // 常量池
    private ConstantPool constantPool;
    // 访问修饰符，不同的int值代表不同的访问修饰符
    private int accessFlags;
    // 类信息值 常量池位置
    private int thisClassIdx;
    // 父类信息值 常量池位置
    private int supperClassIdx;
    // 父接口信息值 常量池位置
    private int[] interfaces;
    // 字段信息
    private MemberInfo[] fields;
    // 方法信息
    private MemberInfo[] methods;
    private AttributeInfo[] attributes;

    /**
     * @param classData class对应的字节码二进制内容
     */
    public ClassFile(byte[] classData) {
        ClassReader reader = new ClassReader(classData);
        this.readAndCheckMagic(reader);
        this.readAndCheckVersion(reader);
        /**
         * u2　　　　　　　constant_pool_count;
         * cp_info　　　　 constant_pool[constant_pool_count-1];
         *
         * cp_info {
         *      u1 tag;
         *      u1 info[];
         * }
         */
        this.constantPool = this.readConstantPool(reader);
//        this.accessFlags = reader.readUint16();
        /*
        访问修饰符（十六进制表示）
        ACC_PUBLIC       = 0x0001;
        ACC_PRIVATE      = 0x0002;
        ACC_PROTECTED    = 0x0004;
        ACC_STATIC       = 0x0008;
        ACC_FINAL        = 0x0010;
        ACC_SUPER        = 0x0020;
        ACC_SYNCHRONIZED = 0x0020;
        ACC_VOLATILE     = 0x0040;
        ACC_BRIDGE       = 0x0040;
        ACC_TRANSIENT    = 0x0080;
        ACC_VARARGS      = 0x0080;
        ACC_NATIVE       = 0x0100;
        ACC_INTERFACE    = 0x0200;
        ACC_ABSTRACT     = 0x0400;
        ACC_STRICT       = 0x0800;
        ACC_SYNTHETIC    = 0x1000;
        ACC_ANNOTATION   = 0x2000;
        ACC_ENUM         = 0x4000;
        */
        // u2　　　　　　　access_flags;
        this.accessFlags = reader.readU2();
//        this.thisClassIdx = reader.readUint16();
        // 当前所属类 utf-8常量值 常量池数组索引地址
        // u2　　　　　　　this_class;
        this.thisClassIdx = reader.readU2();
//        this.supperClassIdx = reader.readUint16();
        // 父类
        // u2　　　　　　　super_class;
        this.supperClassIdx = reader.readU2();
        // 实现的接口们
        // u2　　　　　　　interfaces_count;
        // u2　　　　　　　interfaces[interfaces_count] 因为是常量池地址所以一个u2就足够了
        this.interfaces = reader.readUint16s();
        /**
         * u2　　　　　　　fields_count;
         * field_info 　　　fields[fields_count];
         *
         * field_info {
         *      u2 access_flags;
         *      u2 name_index;
         *      u2 descriptor_index;
         *      u2 attributes_count;
         *      attribute_info attributes[attributes_count];
         * }
         *
         * attribute_info {
         *      u2 attribute_name_index;
         *      u4 attribute_length;
         *      u1 info[attribute_length];
         * }
         */
        this.fields = MemberInfo.readMembers(reader, constantPool);
        /**
         * u2　　　　　　　methods_count;
         * method_info　　methods[methods_count];
         *
         * method_info {
         *      u2 access_flags;
         *      u2 name_index;
         *      u2 descriptor_index;
         *      u2 attributes_count;
         *      attribute_info attributes[attributes_count];
         * }
         *
         * attribute_info {
         *      u2 attribute_name_index;
         *      u4 attribute_length;
         *      u1 info[attribute_length];
         * }
         */
        this.methods = MemberInfo.readMembers(reader, constantPool);
        /**
         * attribute_info　　attributes[attributes_count]
         *
         * attribute_info {
         *      u2 attribute_name_index;
         *      u4 attribute_length;
         *      u1 info[attribute_length];
         * }
         */
        this.attributes = AttributeInfo.readAttributes(reader, constantPool);
    }

    private void readAndCheckMagic(ClassReader reader) {
//        long magic = reader.readUint32();
        long magic = reader.readU4();
        if (magic != (0xCAFEBABE & 0x0FFFFFFFFL)) {
            throw new ClassFormatError("magic!");
        }
    }

    private void readAndCheckVersion(ClassReader reader) {
//        this.minorVersion = reader.readUint16();
//        this.majorVersion = reader.readUint16();
        this.minorVersion = reader.readU2();
        this.majorVersion = reader.readU2();
        switch (this.majorVersion) {
            case 45:
                return;
            case 46:
            case 47:
            case 48:
            case 49:
            case 50:
            case 51:
            case 52:
                if (this.minorVersion == 0)
                    return;
        }
        throw new UnsupportedClassVersionError();
    }

    private ConstantPool readConstantPool(ClassReader reader) {
        return new ConstantPool(reader);
    }

    public int minorVersion() {
        return this.minorVersion;
    }

    public int majorVersion() {
        return this.majorVersion;
    }

    public ConstantPool constantPool() {
        return this.constantPool;
    }

    public int accessFlags() {
        return this.accessFlags;
    }

    public AttributeInfo[] attributeInfo() {
        return this.attributes;
    }

    public MemberInfo[] fields() {
        return this.fields;
    }

    public MemberInfo[] methods() {
        return this.methods;
    }

    public String className() {
        return this.constantPool.getClassName(this.thisClassIdx);
    }

    public String superClassName() {
        if (this.supperClassIdx <= 0) return "";
        return this.constantPool.getClassName(this.supperClassIdx);
    }

    public String[] interfaceNames() {
        String[] interfaceNames = new String[this.interfaces.length];
        for (int i = 0; i < this.interfaces.length; i++) {
            interfaceNames[i] = this.constantPool.getClassName(interfaces[i]);
        }
        return interfaceNames;
    }
}
